#include "Real.hpp"

// Inicializa por defecto en 0
Real::Real()
{
	number = 0;
}

Real::~Real()
{
}

// Casteo de double a Real de t dígitos
Real::Real( double n )
{
	// Inicializa la estructura	
	number = n;

	if( Real::roundNumber )
		// redondea a t bits
		number = Real::round( number );
	
	// trunca la mantisa a t bits
	number = Real::truncate( number );
}

// Suma
Real Real::operator+ ( const Real& r ) const
{
	return Real(this->number+r.number);
}

// Resta
Real Real::operator- ( const Real& r ) const
{
	return Real(this->number-r.number);
}

// Multiplicación
Real Real::operator* ( const Real& r ) const
{
	return Real(this->number*r.number);
}
Real operator* ( const double n, const Real& r )
{
	return r*n;
}

// División
Real Real::operator/ ( const Real& r ) const
{
	return Real(this->number/r.number);
}
Real operator/ ( const double n, const Real& r )
{
	return Real(n)/r;
}

// Adición
Real& Real::operator+= ( const Real& r )
{
	this->number += r.number;
	
	if( Real::roundNumber )
		// redondea a t bits
		this->number = Real::round( this->number );
	
	// trunca la mantisa a t bits
	this->number = Real::truncate( this->number );

	return *this;
}

// Substracción
Real& Real::operator-= ( const Real& r )
{
	this->number -= r.number;
	
	if( Real::roundNumber )
		// redondea a t bits
		this->number = Real::round( this->number );
	
	// trunca la mantisa a t bits
	this->number = Real::truncate( this->number );

	return *this;
}

// Multiplicación del número
Real& Real::operator*= ( const Real& r )
{
	this->number *= r.number;
	
	if( Real::roundNumber )
		// redondea a t bits
		this->number = Real::round( this->number );
	
	// trunca la mantisa a t bits
	this->number = Real::truncate( this->number );

	return *this;
}

// División del número
Real& Real::operator/= ( const Real& r )
{
	this->number /= r.number;
	
	if( Real::roundNumber )
		// redondea a t bits
		this->number = Real::round( this->number );
	
	// trunca la mantisa a t bits
	this->number = Real::truncate( this->number );

	return *this;
}

double Real::toDouble() const
{
	return number;
}

void Real::precision( unsigned int t )
{
	Real::digits = t;
}

unsigned int Real::precision()
{
	return Real::digits;
}

double Real::truncate( double n ) 
{
	// máscara que deja signo,exp y t bits en 1
	long long  mask = 0xFFFFFFFFFFFFFFFF << (52-Real::digits);

	// interpreta el double como una tira de bits y aplica la máscara
	long long* ptr = (long long*)&n;
	*ptr &= mask;

	return n;
}

double Real::round( double n ) 
{
	long long* ptr;

	// máscara que extrae el exponente y el signo del número
	long long mask_exp = 0xFFF0000000000000;

	// exp = 2^exp(n)
	double exp = n;
	ptr   = (long long*)&exp;
	*ptr &= mask_exp;

	// k = 2^exp(n) * (1 + 2^-(t+1))
	double k = n;
	ptr   = (long long*)&k;
	*ptr &= mask_exp | (1ll << (51-Real::digits));

	// k-exp = 2^(exp(n)-t-1), es decir
	// suma a n un 1 en el bit t+1
	n += k - exp;

	return n;
}

std::ostream& operator<< ( std::ostream& out, const Real& r )
{
	out << r.toDouble();
	return out;
}

bool Real::roundNumber = true;
unsigned int Real::digits = 51;

